package own.stu.jobgib.playown.juc.threadLocal;

import lombok.extern.slf4j.Slf4j;

import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

@Slf4j
public class TheadLocalTest {

    public static void main(String[] args) throws InterruptedException {
//        weakReferenceTest();
//        weakReferenceTest_v2();
//        weakReferenceTest_v3();

        // threadLocalTest();

        testMemoryLeak();
    }

    // TODO
    private static void testMemoryLeak() throws InterruptedException {

        ThreadLocal<byte[]> t1 = ThreadLocal.withInitial(() -> new byte[1021 * 1024 * 2]);
        new Thread(() -> {
            byte[] datas = new byte[1021 * 1024 * 10];
            try {
                t1.set(datas);
                TimeUnit.SECONDS.sleep(1);
                datas = null;
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                try {
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                }
                System.out.println(" ----- ");
            }
        }, "t - " + 0).start();

        System.out.println("---main");
        System.gc();

    }

    private static void testMemoryLeak_ii(int n) throws InterruptedException {

        ThreadLocal<byte[]> t1 = ThreadLocal.withInitial(() -> new byte[1021 * 1024 * 2]);

        for (int i = 0; i < n; i++) {
            final ThreadLocal<byte[]>[] finalT = new ThreadLocal[]{t1};
            new Thread(() -> {
                byte[] bytes = finalT[0].get();
                finalT[0] = null;
                try {
                    TimeUnit.MINUTES.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(bytes);
            }, "t - " + i).start();
        }
        t1 = null;
        TimeUnit.SECONDS.sleep(5);
        System.out.println(" --- start gc --- ");
        System.gc();
        System.out.println(" --- gc over --- ");
        TimeUnit.SECONDS.sleep(5);
        TimeUnit.MINUTES.sleep(10);
    }

    // -verbose:gc -Xmx10m
    /*
        2023-07-04 21:48:44.006 INFO  own.stu.jobgib.playown.juc.threadLocal.TheadLocalTest -  reference val: java.lang.Object@604ed9f0
        [GC (Allocation Failure)  4353K->3689K(9216K), 0.0012126 secs]
        SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]
        2023-07-04 21:48:44.010 INFO  own.stu.jobgib.playown.juc.threadLocal.TheadLocalTest -  reference val: null
     */
    public static void weakReferenceTest() {
        WeakReference<Object> reference = new WeakReference<>(new Object());
        log.info(" reference val: {}", reference.get());

        List<Byte[]> list = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            list.add(new Byte[1024]);
        }
        sleepLoop((v) -> log.info(" reference val: {}", reference.get()));
    }

    /**
     * jconsole 手动触发gc
     * <p>
     * 2023-07-04 21:55:02.275 INFO  own.stu.jobgib.playown.juc.threadLocal.TheadLocalTest -  reference val: java.lang.Object@604ed9f0
     * 2023-07-04 21:55:03.276 INFO  own.stu.jobgib.playown.juc.threadLocal.TheadLocalTest -  reference val: null
     */
    public static void weakReferenceTest_v2() {
        WeakReference<Object> reference = new WeakReference<>(new Object());
        log.info(" reference val: {}", reference.get());

        sleepLoop((v) -> log.info(" reference val: {}", reference.get()));
    }

    public static void weakReferenceTest_v3() {
        Object o = new Object();
        WeakReference<Object> reference = new WeakReference<>(o);
        log.info(" reference val: {}", reference.get());
        o = null; // 这行代码，没有释放对 对象o的引用时，下一行代码会获取到值
        sleepLoop((v) -> log.info(" reference val: {}", reference.get()));
    }

    public static void threadLocalTest() throws InterruptedException {
        Holder holder = new Holder();

        Thread thread = new Thread(() -> {
            Integer val = 222;

            ThreadLocal<Integer> local = holder.getLocal();

            local.set(val);

            log.info("{} : {}", Thread.currentThread().getName(), local.get());

            local = null;
            System.gc();
            Thread currentThread = Thread.currentThread();
            try {
                Field inheritableThreadLocals = currentThread.getClass().getDeclaredField("threadLocals");
                inheritableThreadLocals.setAccessible(true);
                Object o = inheritableThreadLocals.get(currentThread); // ThreadLocal.ThreadLocalMap 实例引用

                //Class<?> innerClass = Class.forName("full.package.name.OuterClass$InnerClass")
                Class<?> innerClass = Class.forName("java.lang.ThreadLocal$ThreadLocalMap");
                Field table = innerClass.getDeclaredField("table");
                table.setAccessible(true);
                Object tableO = table.get(o);
                System.out.println(tableO); // value = 222, reference = null, 内存已经泄漏

            } catch (Exception e) {
                e.printStackTrace();
            }


        }, "t-1");
        thread.start();

        TimeUnit.MILLISECONDS.sleep(100);
        holder.setNull();

        thread.join();
    }

    static class Holder {
        ThreadLocal<Integer> local = ThreadLocal.withInitial(() -> {
            log.info(" ------ {}", Thread.currentThread().getName());
            return 1;
        });

        public ThreadLocal<Integer> getLocal() {
            return local;
        }

        public void setNull() {
            local = null;
        }
    }

    private static void sleepLoop(Consumer<Void> consumer) {
        for (int i = 0; i < 100; i++) {
            try {
                TimeUnit.SECONDS.sleep(1);
                consumer.accept(null);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
